package com.bwie.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bwie.es.EsLog;
import com.bwie.pojo.TbLog;
import com.bwie.service.TbLogService;
import com.bwie.mapper.TbLogMapper;
import com.bwie.utils.R;
import com.bwie.vo.LogPageVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.IndexOperations;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.kafka.support.Acknowledgment;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

/**
* @author andy
* @description 针对表【tb_log(日志表)】的数据库操作Service实现
* @createDate 2025-05-05 10:05:33
*/
@Service
@Slf4j
public class TbLogServiceImpl extends ServiceImpl<TbLogMapper, TbLog>
    implements TbLogService{

    @Autowired
    TbLogMapper tbLogMapper;

    @Autowired
    ElasticsearchRestTemplate elasticsearchRestTemplate;

    /**
     * @description 项目启动时，检查日志索引，如果索引不存在，创建索引
     * @PostConstruct 这个注解，表示项目启动时，执行这个方法
     * @params []
     * @return void
     * @author 军哥
     * @date 2025/5/5 10:59
     */
    @PostConstruct
    public void readLogEs() {
        System.out.println("项目启动时，检查日志索引，如果索引不存在，创建索引");
        IndexOperations indexOps = elasticsearchRestTemplate.indexOps(EsLog.class);
        boolean exists = indexOps.exists();
        if(!exists) {
            System.out.println("索引不存在，创建索引");

            indexOps.create();

            Document mapping = indexOps.createMapping();
            indexOps.putMapping(mapping);
        }
    }


    @KafkaListener(topics = "topic-log")
    public void recvLogMessage(String message, Acknowledgment acknowledgment) {
        log.info("Kafka接收到[日志]消息:" + message);

        // 消费消息
        TbLog tbLog = JSON.parseObject(message, TbLog.class);
        tbLogMapper.insert(tbLog);

        // 把日志保存到ES中
        EsLog esLog = BeanUtil.toBean(tbLog, EsLog.class);
        elasticsearchRestTemplate.save(esLog);

        // 确认消息
        acknowledgment.acknowledge();
    }

    @Override
    public R getLogList(LogPageVo logPageVo) {
        //-- 构造分页参数（ES的分页从0开始，而MyBaits是从1开始）
        Integer pageNum = 0;
        if(logPageVo.getPageNum() != null && logPageVo.getPageNum() > 0) {
            pageNum = logPageVo.getPageNum() - 1;
        }
        PageRequest pageRequest = PageRequest.of(pageNum, logPageVo.getPageSize());

        //-- 构造搜索条件
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();

        // 添加关键字查询
        // matchQuery 分词查询
        // rangeQuery 区间查询
        // termQuery 精确查询（不分词查询）
        if(StringUtils.isNotEmpty(logPageVo.getKeyWord())) {
            boolQueryBuilder.should(
                    QueryBuilders.matchQuery("logContent", logPageVo.getKeyWord())
            );
        }

        // 添加时间区间
        // gte  == greater than or equal   >= 大于等于
        // lte  == less than or equal   <= 小于等于
        // gt   == greater than   > 大于
        // lt   == less than   < 小于
        // equals == 等于
        if(StringUtils.isNotEmpty(logPageVo.getBeginTime())) {
            boolQueryBuilder.must(
                    QueryBuilders.rangeQuery("createTime")
                            .format("yyyy-MM-dd HH:mm:ss")
                            .timeZone("GMT+8")
                            .gte(logPageVo.getBeginTime())
            );
        }
        if(StringUtils.isNotEmpty(logPageVo.getEndTime())) {
            boolQueryBuilder.must(
                    QueryBuilders.rangeQuery("createTime")
                            .format("yyyy-MM-dd HH:mm:ss")
                            .timeZone("GMT+8")
                            .lte(logPageVo.getEndTime())
            );
        }

        // 添加精确查询
        if(logPageVo.getLogType() != null) {
            boolQueryBuilder.must(
                    QueryBuilders.termQuery("logType", logPageVo.getLogType())
            );
        }

        // 添加高亮
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("logContent").preTags("<font color='red'>").postTags("</font>");

        // 排序
        FieldSortBuilder sortBuilder = SortBuilders.fieldSort("createTime").order(SortOrder.DESC);

        //-- 开始查询
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        nativeSearchQueryBuilder
                .withPageable(pageRequest)                  // 分页
                .withSort(sortBuilder)                      // 排序
                .withHighlightBuilder(highlightBuilder)     // 高亮
                .withQuery(boolQueryBuilder);               // 多条件

        SearchHits<EsLog> searchHits = elasticsearchRestTemplate.search(nativeSearchQueryBuilder.build(), EsLog.class);

        //-- 读取查询结果
        Long total = searchHits.getTotalHits();
        ArrayList<EsLog> records = new ArrayList<>();

        for (SearchHit<EsLog> searchHit : searchHits.getSearchHits()) {
            EsLog esLog = searchHit.getContent();

            // 获取高亮字段
            List<String> logContentList = searchHit.getHighlightField("logContent");
            if(logContentList != null && logContentList.size()>0) {
                String logContent = logContentList.get(0);
                esLog.setLogContent(logContent);
            }

            records.add(esLog);
        }

        //-- 返回查询结果
        HashMap<String, Object> map = new HashMap<>();
        map.put("total", total);
        map.put("records", records);

        return R.OK(map);
    }
}




